home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tricks of the Mac Game Programming Gurus
/
TricksOfTheMacGameProgrammingGurus.iso
/
More Source
/
C⁄C++
/
Marathon Map Viewer
/
@Source
/
levelView.cpp
< prev
next >
Wrap
Text File
|
1995-06-06
|
16KB
|
581 lines
/*-----------------------------------------------------------------
Copyright ©1994 Steve Israelson
This file contains the code for the "levelView" class.
This class handles the displaying of a level in a view.
-----------------------------------------------------------------*/
#include "levelView.h"
#include "commandNumbers.h"
#include "myApplication.h"
#include "levelUtils.h"
/*-----------------------------------------------------------------
Make a new levelView. Called from the URegistrar.
-----------------------------------------------------------------*/
levelView *levelView::CreateViewStream(LStream *inStream)
{
return (new levelView(inStream));
}
/*-----------------------------------------------------------------
Initialize the view to a known state.
-----------------------------------------------------------------*/
levelView::levelView(LStream *inStream) : LView(inStream)
{
theLevelData = nil;
scaleFactor = 63;
showGrid = false;
showPoints = true;
showLines = true;
showObjects = false;
Point where = {0x7fff, 0x7fff};
setScale(64, where);
if (!IsTarget())
SwitchTarget(this);
}
/*-----------------------------------------------------------------
Initialize the view to a known state.
-----------------------------------------------------------------*/
levelView::levelView(const SPaneInfo &inPaneInfo, const SViewInfo &inViewInfo) : LView(inPaneInfo, inViewInfo)
{
theLevelData = nil;
scaleFactor = 63;
showGrid = false;
showPoints = true;
showLines = true;
showObjects = false;
Point where = {0x7fff, 0x7fff};
setScale(64, where);
if (!IsTarget())
SwitchTarget(this);
}
/*-----------------------------------------------------------------
Clean up when we are disposed.
-----------------------------------------------------------------*/
levelView::~levelView()
{
}
/*-----------------------------------------------------------------
After the window is created, lets do some stuff.
-----------------------------------------------------------------*/
void levelView::FinishCreate()
{
fixImageSize();
LView::FinishCreate();
Refresh();
}
/*-----------------------------------------------------------------
This is so that the scroll bars will work properly.
They use the image size to decide if scrolling is possible.
The image is the area that our entire level will take up.
When this view is put into a ScrollView, the ScrollView will
set up the scroll bars so we can view all of the image, even
if it doesn't fit in the window all at once.
-----------------------------------------------------------------*/
void levelView::fixImageSize(void)
{
long width = offset + 32768L / scaleFactor;
long height = offset + 32768L / scaleFactor;
ResizeImageTo(width, height, false);
}
/*-----------------------------------------------------------------
Zoom centered on the point where, unless where = 0x7fff, 0x7fff
There has to be an easier way to do this!
-----------------------------------------------------------------*/
void levelView::setScale(long newScale, Point where)
{
Rect frame;
long centerX, centerY, newCenterX, newCenterY;
long oldScale = scaleFactor;
if (newScale < 4)
newScale = 4;
if (newScale > 1024)
newScale = 1024;
offset = 0x8000L / scaleFactor;
FocusDraw();
CalcLocalFrameRect(frame);
// calc center in mara coords
if (where.h == 0x7fff && where.v == 0x7fff)
{
centerX = (frame.left + frame.right) / 2 * scaleFactor;
centerY = (frame.top + frame.bottom) / 2 * scaleFactor;
}
else
{
centerX = where.h * scaleFactor;
centerY = where.v * scaleFactor;
}
scaleFactor = newScale;
offset = 0x8000L / scaleFactor;
fixImageSize();
// fix up how big the window can be.
// if we zoom way out, we dont want the window to be bigger than us.
SDimension16 newSize = {65535L / scaleFactor, 65535L / scaleFactor};
LWindow *owner = LWindow::FetchWindowObject(GetMacPort());
if (owner && owner->IsVisible())
{
Rect theRect = (*((WindowPeek)GetMacPort())->contRgn)->rgnBBox;
owner->SetStandardSize(newSize);
if (theRect.right - theRect.left > newSize.width + 15)
theRect.right = theRect.left + newSize.width + 15;
if (theRect.bottom - theRect.top > newSize.height + 15)
theRect.bottom = theRect.top + newSize.height + 15;
owner->DoSetBounds(theRect);
owner->GetMinMaxSize(theRect);
theRect.right = newSize.width + 15;
theRect.bottom = newSize.height + 15;
owner->SetMinMaxSize(theRect);
}
SDimension32 theImageSize;
GetImageSize(theImageSize);
CalcLocalFrameRect(frame);
// calc width in marathon coords
newCenterX = (frame.right - frame.left) / 2 * scaleFactor;
newCenterY = (frame.bottom - frame.top) / 2 * scaleFactor;
// scroll to the center point
long newXpos = (centerX - newCenterX) / scaleFactor;
long newYpos = (centerY - newCenterY) / scaleFactor;
if (newXpos < 0)
newXpos = 0;
if (newYpos < 0)
newYpos = 0;
if (newXpos + (frame.right - frame.left) > theImageSize.width)
newXpos = theImageSize.width - (frame.right - frame.left);
if (newYpos + (frame.bottom - frame.top) > theImageSize.height)
newYpos = theImageSize.height - (frame.bottom - frame.top);
ScrollImageTo(newXpos, newYpos, false);
Refresh();
}
/*-----------------------------------------------------------------
Some one will eventually give us a level to display.
There may be a better way to handle this, but this method
allows us to make one view that can display different
levels in it, just by calling this when a new level
needs to be displayed.
-----------------------------------------------------------------*/
void levelView::setLevelData(levelData *newLevelData)
{
theLevelData = newLevelData;
Point mapCenter;
long centerX, centerY;
getMapCenter(theLevelData, ¢erX, ¢erY);
mapCenter.h = offset + centerX / scaleFactor;
mapCenter.v = offset + centerY / scaleFactor;
setScale(scaleFactor, mapCenter);
}
/*-----------------------------------------------------------------
Handle a mouse click. This is where you can do different
things, depending on what tool the user has selected.
-----------------------------------------------------------------*/
void levelView::ClickSelf(const SMouseDownEvent &inMouseDown)
{
FocusDraw();
if (!IsTarget())
{
SwitchTarget(this);
}
// short tool = theApp->theToolPalette->curTool;
Boolean shiftState = (inMouseDown.macEvent.modifiers & shiftKey) ? true: false;
// switch (tool)
// {
// case tool_arrow:
// write code for handling clicks with the arrow tool...
}
/*-----------------------------------------------------------------
Find out if menu commands are allowed. This is called when
the user clicks in the menu bar.
-----------------------------------------------------------------*/
void levelView::FindCommandStatus(
CommandT inCommand,
Boolean &outEnabled,
Boolean &outUsesMark,
Char16 &outMark,
Str255 outName)
{
outUsesMark = false;
switch (inCommand)
{
case cmd_toggleGrid:
outUsesMark = true;
if (showGrid)
outMark = ''; /* check mark */
else
outMark = ' ';
outEnabled = true;
break;
case cmd_togglePoints:
outUsesMark = true;
if (showPoints)
outMark = ''; /* check mark */
else
outMark = ' ';
outEnabled = true;
break;
case cmd_toggleLines:
outUsesMark = true;
if (showLines)
outMark = ''; /* check mark */
else
outMark = ' ';
outEnabled = true;
break;
case cmd_toggleObjects:
outUsesMark = true;
if (showObjects)
outMark = ''; /* check mark */
else
outMark = ' ';
outEnabled = true;
break;
case cmd_zoomIn:
case cmd_zoomOut:
outEnabled = true;
break;
case cmd_Nothing:
default:
LCommander::FindCommandStatus(inCommand, outEnabled, outUsesMark,
outMark, outName);
break;
}
}
/*-----------------------------------------------------------------
Do a menu command. This is called after the user selects
a command from the menu.
-----------------------------------------------------------------*/
Boolean levelView::ObeyCommand(CommandT inCommand, void *ioParam)
{
Boolean cmdHandled = true;
switch (inCommand)
{
case cmd_togglePoints:
showPoints = !showPoints;
Refresh();
break;
case cmd_toggleLines:
showLines = !showLines;
Refresh();
break;
case cmd_toggleObjects:
showObjects = !showObjects;
Refresh();
break;
case cmd_toggleGrid:
showGrid = !showGrid;
Refresh();
break;
case cmd_zoomIn:
Point where1 = {0x7fff, 0x7fff};
setScale(scaleFactor - 8, where1);
break;
case cmd_zoomOut:
Point where2 = {0x7fff, 0x7fff};
setScale(scaleFactor + 8, where2);
break;
case cmd_Nothing:
default:
return LCommander::ObeyCommand(inCommand, ioParam);
break;
}
return cmdHandled;
}
/*-----------------------------------------------------------------
Draw this view. This gets called by PowerPlant when it needs
part or all of the view re-drawn.
-----------------------------------------------------------------*/
void levelView::DrawSelf()
{
Rect frame;
RGBColor greyColor = {10000, 10000, 10000};
CalcLocalFrameRect(frame);
RGBForeColor(&greyColor);
PaintRect(&frame);
ForeColor(blackColor);
drawPolygons();
if (showGrid)
drawGrid();
if (showLines)
drawLines();
if (showPoints)
drawPoints();
if (showObjects)
drawObjects();
PenNormal();
}
/*-----------------------------------------------------------------
Draw a grid. Nothing fancy.
-----------------------------------------------------------------*/
void levelView::drawGrid(void)
{
long scaledX = 0, maxX, x;
Rect frame;
ForeColor(redColor);
PenPat(&qd.gray);
CalcLocalFrameRect(frame);
if (frame.right > frame.bottom)
maxX = frame.right;
else
maxX = frame.bottom;
for (x = 0; scaledX < maxX; x += 4096)
{
scaledX = x / scaleFactor;
MoveTo(scaledX, 0);
LineTo(scaledX, 32000);
}
for (x = 0, scaledX = 0; scaledX < maxX; x += 4096)
{
scaledX = x / scaleFactor;
MoveTo(0, scaledX);
LineTo(32000, scaledX);
}
PenNormal();
}
/*-----------------------------------------------------------------
Draw all the objects.
-----------------------------------------------------------------*/
void levelView::drawObjects(void)
{
for (int x = 0; x < theLevelData->numObjects; ++x)
drawObject(x);
}
/*-----------------------------------------------------------------
Draw one object.
-----------------------------------------------------------------*/
void levelView::drawObject(short x)
{
RGBColor objectColor = {65535, 0, 0};
Rect theRect;
theRect.left = offset + theLevelData->theObjects[x].x / scaleFactor - 4;
theRect.top = offset + theLevelData->theObjects[x].y / scaleFactor - 4;
theRect.right = theRect.left + 4 * 2;
theRect.bottom = theRect.top + 4 * 2;
short angle = (theLevelData->theObjects[x].facing * 360L) / 512L + 20 + 90;
InsetRect(&theRect, -1, -1);
ForeColor(blackColor);
PaintOval(&theRect);
InsetRect(&theRect, 1, 1);
switch (theLevelData->theObjects[x].type)
{
case object_monster:
objectColor.red = 65535;
objectColor.green = 0;
objectColor.blue = 0;
RGBForeColor(&objectColor);
break;
case object_scenery:
objectColor.red = 65535;
objectColor.green = 65535;
objectColor.blue = 65535;
RGBForeColor(&objectColor);
break;
case object_weapon:
objectColor.red = 0;
objectColor.green = 65535;
objectColor.blue = 0;
RGBForeColor(&objectColor);
break;
case object_player:
objectColor.red = 0;
objectColor.green = 0;
objectColor.blue = 65535;
RGBForeColor(&objectColor);
break;
case object_goal:
objectColor.red = 65535;
objectColor.green = 65535;
objectColor.blue = 0;
RGBForeColor(&objectColor);
break;
}
PaintArc(&theRect, angle, 320);
}
/*-----------------------------------------------------------------
Draw the lines. Draws light lines when there is no wall.
-----------------------------------------------------------------*/
void levelView::drawLines(void)
{
RGBColor greenColor = {0, 65535, 0};
RGBColor greyColor = {40000, 40000, 40000};
short lineSize = 1;
for (int x = 0; x < theLevelData->numLines; ++x)
{
LINSdata *theLine = &theLevelData->theLines[x];
if (theLine->leftPoly != -1 && theLine->rightPoly != -1)
{
// if the floors are the same height, then there is no wall
if (theLevelData->thePolys[theLine->leftPoly].floorHeight ==
theLevelData->thePolys[theLine->rightPoly].floorHeight)
{
PenSize(lineSize, lineSize);
RGBForeColor(&greyColor);
}
else
{
PenNormal();
PenSize(lineSize, lineSize);
RGBForeColor(&greenColor);
}
}
else
{ // if there is no polygon on one side, the there must be a wall
PenNormal();
PenSize(lineSize ,lineSize);
RGBForeColor(&greenColor);
}
// actually draw the line now that its color has been determined
PNTSdata *pointA, *pointB;
pointA = &theLevelData->thePoints[theLine->pointA];
pointB = &theLevelData->thePoints[theLine->pointB];
MoveTo(offset + pointA->x / scaleFactor, offset + pointA->y / scaleFactor);
LineTo(offset + pointB->x / scaleFactor, offset + pointB->y / scaleFactor);
}
ForeColor(blackColor);
PenNormal();
}
/*-----------------------------------------------------------------
Draw some cool points. Make a circle with a thin black line
around it.
-----------------------------------------------------------------*/
void levelView::drawPoints(void)
{
short x;
Rect theRect;
RGBColor greenColor = {0, 65535, 0};
short pointSize = 2;
PenNormal();
for (x = 0; x < theLevelData->numPoints; ++x)
{
PNTSdata *thePoint;
thePoint = &theLevelData->thePoints[x];
theRect.left = offset + thePoint->x / scaleFactor - pointSize;
theRect.top = offset + thePoint->y / scaleFactor - pointSize;
theRect.right = theRect.left + (pointSize << 1);
theRect.bottom = theRect.top + (pointSize << 1);
ForeColor(blackColor);
InsetRect(&theRect, -1, -1);
PaintOval(&theRect);
RGBForeColor(&greenColor);
InsetRect(&theRect, 1, 1);
PaintOval(&theRect);
}
}
/*-----------------------------------------------------------------
Make and draw a polygon.
-----------------------------------------------------------------*/
void levelView::drawPolygons(void)
{
RGBColor liteColor = {0, 0, 65535};
PolyHandle thePolyHandle;
RGBForeColor(&liteColor); // default color
for (int x = 0; x < theLevelData->numPolys; ++x)
{
thePolyHandle = makePolyPolygon(x);
if (thePolyHandle)
{
// paint poly
PaintPoly(thePolyHandle);
// dispose poly
KillPoly(thePolyHandle);
}
PenNormal();
}
ForeColor(blackColor);
}
/*-----------------------------------------------------------------
Make a quickdraw polygon for a poly.
-----------------------------------------------------------------*/
PolyHandle levelView::makePolyPolygon(long thePoly)
{
if (thePoly == -1 || thePoly >= theLevelData->numPolys)
return nil;
// open poly here
PolyHandle thePolyHandle = OpenPoly();
Boolean first = true;
short firstX, firstY;
short *vertexIndex;
short vertexCount;
vertexIndex = theLevelData->thePolys[thePoly].vertexIndex;
vertexCount = theLevelData->thePolys[thePoly].vertexCount;
for (int y = 0; y < vertexCount; ++y)
{
PNTSdata *pointA;
pointA = &theLevelData->thePoints[vertexIndex[y]];
if (first)
{
first = false;
firstX = offset + pointA->x / scaleFactor;
firstY = offset + pointA->y / scaleFactor;
MoveTo(firstX, firstY);
}
else
LineTo(offset + pointA->x / scaleFactor, offset + pointA->y / scaleFactor);
}
// close poly here
LineTo(firstX, firstY);
ClosePoly(); // closes the fisrt and last line as well
return thePolyHandle;
}